﻿#include "SoftRenderer.h"
#include "ClipSpaceCull.h"
#include "PixelShader.h"
#include "Rasterization.h"
#include "VertexShader.h"

/////////////////////////////////////////////////////
// USoftRenderer

USoftRenderer::USoftRenderer(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	RenderMode = ESoftRendererRenderMode::Wireframe;

	bEnableDepthTest = true;
	bEnableEarlyZ = true;

	DepthBufferType = ESoftRendererDepthBufferType::ZBuffer;
	
	ViewportSize = FIntPoint(1280, 720);
	FrameBuffer = nullptr;
	DepthStencilBuffer = nullptr;
	RenderScene = nullptr;
}

void USoftRenderer::InitRenderer()
{
	if (!IsValid(FrameBuffer))
	{
		FrameBuffer = NewObject<UFrameBuffer>();
		FrameBuffer->Resize(FMath::Max(2, ViewportSize.X), FMath::Max(2, ViewportSize.Y));
	}

	if (!IsValid(DepthStencilBuffer))
	{
		DepthStencilBuffer = NewObject<UDepthStencilBuffer>();
		DepthStencilBuffer->Resize(FMath::Max(2, ViewportSize.X), FMath::Max(2, ViewportSize.Y));
	}

	if (IsValid(RenderScene))
	{
		// 创建渲染对象
		for (int32 Index = 0, Count = RenderScene->OpaqueRenderObjectsClasses.Num(); Index < Count; ++Index)
		{
			if (RenderScene->OpaqueRenderObjectsClasses[Index])
			{
				RenderScene->OpaqueRenderObjects.Emplace(NewObject<URenderObject>(RenderScene, RenderScene->OpaqueRenderObjectsClasses[Index]));
			}
		}
	}
}

void USoftRenderer::Render()
{
	if (!IsValid(FrameBuffer))
		return;

	if (!IsValid(DepthStencilBuffer))
		return;

	if (!IsValid(RenderScene))
		return;

	// 1 视口变化时Resize下FrameBuffer
	FrameBuffer->Resize(ViewportSize.X, ViewportSize.Y);
	DepthStencilBuffer->Resize(ViewportSize.X, ViewportSize.Y);

	// 2 将上一帧渲染的颜色数据用指定颜色清空
	FrameBuffer->Clear(ClearColor);
	DepthStencilBuffer->Clear(DepthBufferType);

	// 3 计算视口变换矩阵
	//    这里要乘以一个额外的矩阵原因
	//       1 采用UE的坐标系，Z向上  X屏幕向内  Y向右
	//       2 需要转换为DirectX的左手坐标系，需要再旋转下
	const FMatrix ViewRotationMatrix = FInverseRotationMatrix(RenderCamera.Rotation) * FMatrix(
		FPlane(0,	0,	1,	0),
		FPlane(1,	0,	0,	0),
		FPlane(0,	1,	0,	0),
		FPlane(0,	0,	0,	1));
	
	const FMatrix WorldToViewMatrix = FTranslationMatrix(-RenderCamera.ViewOrigin) * ViewRotationMatrix;

	// 4 计算投影矩阵
	const FMatrix ProjectMatrix = CalculateProjectionMatrix();
	
	// 5 逐个渲染不透明物体
	for (const auto& RenderObject : RenderScene->OpaqueRenderObjects)
	{
		if (!IsValid(RenderObject))
			continue;
		
		DrawPrimitive(RenderObject, WorldToViewMatrix, ProjectMatrix);
	}
}

static TArray<FVertexToPixelInterpolants> TempVertexToPixelInterpolants;

void USoftRenderer::DrawPrimitive(URenderObject* RenderObject, const FMatrix& WorldToViewMatrix, const FMatrix& ProjectionMatrix)
{
	// 1 获取顶点着色器和像素着色器
	USoftRendererShader* VertexShader = RenderObject->Material.VertexShader;
	if (!IsValid(VertexShader) && RenderObject->Material.VertexShaderClass)
	{
		RenderObject->Material.VertexShader = NewObject<UVertexShader>(RenderObject, RenderObject->Material.VertexShaderClass);
		VertexShader = RenderObject->Material.VertexShader;
	}

	if (!IsValid(VertexShader))
	{
		RenderObject->Material.VertexShader = NewObject<UVertexShader>(RenderObject, UVertexShader::StaticClass());
		VertexShader = RenderObject->Material.VertexShader;
	}

	USoftRendererShader* PixelShader = RenderObject->Material.PixelShader;
	if (!IsValid(PixelShader) && RenderObject->Material.PixelShaderClass)
	{
		RenderObject->Material.PixelShader = NewObject<UPixelShader>(RenderObject, RenderObject->Material.PixelShaderClass);
		PixelShader = RenderObject->Material.PixelShader;
	}

	if (!IsValid(PixelShader))
	{
		RenderObject->Material.PixelShader = NewObject<UPixelShader>(RenderObject, UPixelShader::StaticClass());
		PixelShader = RenderObject->Material.PixelShader;
	}
	
	// 2 设置Shader的内置变量
	FShaderBuiltinVariables BuiltinVariables;
	BuiltinVariables.Matrix_ObjectToWorld = RenderObject->GetLocalToWorld();
	BuiltinVariables.Matrix_WorldToObject = BuiltinVariables.Matrix_ObjectToWorld.InverseFast();
	BuiltinVariables.Matrix_MV = BuiltinVariables.Matrix_ObjectToWorld * WorldToViewMatrix;
	BuiltinVariables.Matrix_MVP = BuiltinVariables.Matrix_MV * ProjectionMatrix;
	BuiltinVariables.Matrix_V = WorldToViewMatrix;
	BuiltinVariables.Matrix_P = ProjectionMatrix;
	BuiltinVariables.T_MV = BuiltinVariables.Matrix_MV.GetTransposed();
	BuiltinVariables.IT_MV = BuiltinVariables.T_MV.InverseFast();

	VertexShader->ShaderBuiltinVariables = &BuiltinVariables;
	PixelShader->ShaderBuiltinVariables = &BuiltinVariables;
	
	// 3 对每一个顶点执行顶点着色器程序
	TempVertexToPixelInterpolants.Reset();
	TempVertexToPixelInterpolants.Reserve(RenderObject->Vertices.Num());
	
	for (auto& Vertex : RenderObject->Vertices)
	{
		TempVertexToPixelInterpolants.Emplace(VertexShader->RunVertexShader(Vertex));
	}

	// 4 执行顶点裁剪，透视除法，三角形光栅化
	for (int32 Index = 0, Count = RenderObject->Indices.Num(); Index < Count; Index += 3)
	{
		const auto& Vertex1 = TempVertexToPixelInterpolants[RenderObject->Indices[Index]];
		const auto& Vertex2 = TempVertexToPixelInterpolants[RenderObject->Indices[Index + 1]];
		const auto& Vertex3 = TempVertexToPixelInterpolants[RenderObject->Indices[Index + 2]];

		// 1 执行齐次裁剪
		// 使用栈分配器在栈上分配数组，避免堆分配，提高性能
		TArray<FVertexToPixelInterpolants, TInlineAllocator<16>> VertexList = {Vertex1, Vertex2, Vertex3};
		FClipSpaceCull::SutherlandHodgeman(VertexList);

		if (VertexList.Num() <= 0)
			continue;
		
		// 2 每一个顶点执行透视除法, 计算屏幕坐标等
		for (int32 VertexIndex = 0, VertexCount = VertexList.Num(); VertexIndex < VertexCount; ++VertexIndex)
		{
			FVertexToPixelInterpolants& Vertex = VertexList[VertexIndex];
			//Vertex.PerspectiveDivisionCPU(ViewportSize);
			Vertex.PerspectiveDivisionGPU(ViewportSize);
		}

		// 3 如果是线渲染模式，直接画线不执行光栅化
		if (RenderMode == ESoftRendererRenderMode::Wireframe)
		{
			for (int32 VertexIndex = 0, VertexCount = VertexList.Num() - 2; VertexIndex < VertexCount; ++VertexIndex)
			{
				const auto& ClipVertex1 = VertexList[0];
				const auto& ClipVertex2 = VertexList[VertexIndex + 1];
				const auto& ClipVertex3 = VertexList[VertexIndex + 2];

				// 计算整数屏幕坐标：加 0.5 的偏移取屏幕像素方格中心对齐，其实就是四舍五入
				const FIntPoint ScreenPosInPixels1 = FIntPoint( static_cast<int32>(ClipVertex1.ScreenPos.X + 0.5f), static_cast<int32>(ClipVertex1.ScreenPos.Y + 0.5f));
				const FIntPoint ScreenPosInPixels2 = FIntPoint( static_cast<int32>(ClipVertex2.ScreenPos.X + 0.5f), static_cast<int32>(ClipVertex2.ScreenPos.Y + 0.5f));
				const FIntPoint ScreenPosInPixels3 = FIntPoint( static_cast<int32>(ClipVertex3.ScreenPos.X + 0.5f), static_cast<int32>(ClipVertex3.ScreenPos.Y + 0.5f));
				
				FrameBuffer->DrawLine(ScreenPosInPixels1.X, ScreenPosInPixels1.Y, ScreenPosInPixels2.X, ScreenPosInPixels2.Y);
				FrameBuffer->DrawLine(ScreenPosInPixels2.X, ScreenPosInPixels2.Y, ScreenPosInPixels3.X, ScreenPosInPixels3.Y);
				FrameBuffer->DrawLine(ScreenPosInPixels1.X, ScreenPosInPixels1.Y, ScreenPosInPixels3.X, ScreenPosInPixels3.Y);
			}

			continue;
		}

		// 4 执行三角形光栅化逻辑
		for (int32 VertexIndex = 0, VertexCount = VertexList.Num() - 2; VertexIndex < VertexCount; ++VertexIndex)
		{
			const auto& ClipVertex1 = VertexList[0];
			const auto& ClipVertex2 = VertexList[VertexIndex + 1];
			const auto& ClipVertex3 = VertexList[VertexIndex + 2];

			//FRasterizationCPU::RasterizeTriangle(this, ClipVertex1, ClipVertex2, ClipVertex3);
			FRasterizationGPU::RasterizeTriangle(this, &ClipVertex1, &ClipVertex2, &ClipVertex3, PixelShader, RenderObject->Material);
		}
	}
}

FMatrix USoftRenderer::CalculateProjectionMatrix() const
{
	float XAxisMultiplier;
	float YAxisMultiplier;
	
	const int32 SizeX = ViewportSize.X;
	const int32 SizeY = ViewportSize.Y;

	FMatrix ProjectionMatrix;

	const bool bMaintainXFOV = RenderCamera.FOVMode == ESoftRendererCameraFOVMode::Horizontal;
	if (bMaintainXFOV)
	{
		// 如果视口宽度大于高度
		XAxisMultiplier = 1.0f;
		YAxisMultiplier = SizeX / static_cast<float>(SizeY);
	}
	else
	{
		// 如果视口高度大于宽度
		XAxisMultiplier = SizeY / static_cast<float>(SizeX);
		YAxisMultiplier = 1.0f;
	}
	
	if (RenderCamera.ProjectionMode == ESoftRendererCameraProjectionMode::Orthographic)
	{
		const float OrthoWidth = (bMaintainXFOV ? RenderCamera.OrthoWidth : RenderCamera.OrthoHeight) / 2.0f / XAxisMultiplier;
		const float OrthoHeight = (bMaintainXFOV ? RenderCamera.OrthoWidth : RenderCamera.OrthoHeight) / 2.0f / YAxisMultiplier;

		constexpr float NearPlane = 0.0f;
		constexpr float FarPlane = WORLD_MAX;

		constexpr float ZScale = 1.0f / (FarPlane - NearPlane);
		constexpr float ZOffset = -NearPlane;

		if (DepthBufferType != ESoftRendererDepthBufferType::ReversedZBuffer)
		{
			ProjectionMatrix = FOrthoMatrix(
				OrthoWidth, 
				OrthoHeight,
				ZScale,
				ZOffset
				);
		}
		else
		{
			ProjectionMatrix = FReversedZOrthoMatrix(
				OrthoWidth, 
				OrthoHeight,
				ZScale,
				ZOffset
				);	
		}
	}
	else
	{
		// Avoid divide by zero in the projection matrix calculation by clamping FOV.
		// Note the division by 360 instead of 180 because we want the half-FOV.
		const float MatrixHalfFOV = FMath::Max(0.001f, RenderCamera.FOV) * (float)PI / 360.0f;
		
		if (DepthBufferType != ESoftRendererDepthBufferType::ReversedZBuffer)
		{
			ProjectionMatrix = FPerspectiveMatrix(
				MatrixHalfFOV,
				MatrixHalfFOV,
				XAxisMultiplier,
				YAxisMultiplier,
				GNearClippingPlane,
				GNearClippingPlane
				);
		}
		else
		{
			ProjectionMatrix = FReversedZPerspectiveMatrix(
				MatrixHalfFOV,
				MatrixHalfFOV,
				XAxisMultiplier,
				YAxisMultiplier,
				GNearClippingPlane,
				GNearClippingPlane
				);
		}
	}
	
	return ProjectionMatrix;
}

/////////////////////////////////////////////////////
